package edu.pku.sei.transformation.editor.model.namespace;

import java.util.ArrayList;
import java.util.Collection;

import org.w3c.dom.Element;

import edu.pku.sei.gefcommon.descriptor.annotation.MethodProperty;
import edu.pku.sei.modeler.model.core.MetaModelElement;
import edu.pku.sei.modeler.model.xml.XMLUtility;
import edu.pku.sei.transformation.editor.model.ModelPackage;

/**
 * An element with a name, a namespace that it belongs to, and maybe its own namespace. 
 * Menmber <b>nameTable</b> can be null, but can't be saved, even it's not null.
 * so every this element is loaded from file, should create a new instant for <b>nameTable</b> 
 * 
 * @author fengchao
 *
 */
public abstract class NamedElement extends MetaModelElement implements INameTable {
	protected String name = "undefine";
	protected NamedElement superNamespace;
	/**
	 * for now, this member can't be saved,
	 * so every time, this element is loaded from file,
	 * this member should be create and register elements in it.
	 */
	protected INameTable nameTable = null;
	/**
	 * whether distinguish elements by element name
	 * if this property is true, if two elements have same superNamespace and same name, we think this two element are same
	 * else, if two elements are two references of one same object, we think this two elements are same.  
	 */
	protected boolean distinguishByName = false;
	private static ENameTableType defaultTableType = ENameTableType.none;
	
	/**
	 * initialize NamedElement with default INameTable type 
	 */
	public NamedElement() {
		this(defaultTableType);
	}
	/**
	 * initialize NamedElement with INameTable of tableType
	 * @param tableType
	 */
	public NamedElement(ENameTableType tableType) {
		if (tableType == ENameTableType.hashTable)
			nameTable = new HashNameTable();
		else if (tableType == ENameTableType.none)
			nameTable = null;
		else 
			nameTable = null;
	}
	@MethodProperty(name = "name", catalog = "Named Element", id = ModelPackage.NAMEDELEMENT_NAME, setter = "setName")
	public String getName() {
		return name;
	}
	public void setName(String name) {
		if (this.name == null || !this.name.equals(name)) {
			String oldValue = this.name;
			this.name = name;
			firePropertyChange(ModelPackage.NAMEDELEMENT_NAME, oldValue, name);
		}
	}
	/**
	 * get namespace this element belongs to
	 * @return
	 */
	public NamedElement getSuperNamespace() {
		return superNamespace;
	}
	/**
	 * set namespace this element belongs to
	 * @param namespace
	 */
	public void setSuperNamespace(NamedElement namespace) {
		this.superNamespace = namespace;
	}

	public Collection<NamedElement> getAllElements() {
		if (nameTable != null)
			return nameTable.getAllElements();
		else 
			return new ArrayList<NamedElement>();
	}
	public NamedElement getElement(String name) {
		if (nameTable != null)
			return nameTable.getElement(name);
		else
			return null;
	}
	public boolean registerName(NamedElement element) {
		if (nameTable != null)
			return nameTable.registerName(element);
		else 
			return false;
	}
	public boolean unregisterName(NamedElement element) {
		if (nameTable != null)
			return nameTable.unregisterName(element);
		else
			return false;
	}
	public boolean unregisterName(String name) {
		if (nameTable != null)
			return nameTable.unregisterName(name);
		else
			return false;
	}
	@Override
	public void serialize(Element root) {
		super.serialize(root);
		XMLUtility.xmlInstance.serializeAttribute(root, ModelPackage.NAMEDELEMENT_NAME, name);
		XMLUtility.xmlInstance.serializeNodeNotContain(root, ModelPackage.NAMEDELEMENT_NAMESPACE, this.superNamespace);
	}
	
	@Override
	public void deSerialize(Element root) {
		super.deSerialize(root);
		this.name = XMLUtility.xmlInstance.deSerializeString(root, ModelPackage.NAMEDELEMENT_NAME);
		this.superNamespace = (NamedElement) XMLUtility.xmlInstance.deSerializeNodeNotContain(root, ModelPackage.NAMEDELEMENT_NAMESPACE, this);
	}
	
	@Override
	public void dynamicSet(String featureID, Object value) {
		if (ModelPackage.NAMEDELEMENT_NAMESPACE.equals(featureID)) {
			this.superNamespace = (NamedElement) value;
		} else {
			super.dynamicSet(featureID, value);			
		}
	}
	
	@Override
	public boolean equals(Object obj) {
		if (distinguishByName) {
			if (obj instanceof NamedElement) {
				NamedElement targetElement = (NamedElement) obj;
				if (this.superNamespace == targetElement.superNamespace
						&& this.name.equals(targetElement.name))
					return true;
				else 
					return false;
			} else {
				return false;
			}			
		} else {
			return super.equals(obj);
		}
	}
}
